/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "napi/native_api.h"

#include "imageknife.h"
#include "imageknife_type.h"
#include "imageknife_task.h"
#include "custom_file_type_parser.h"

static std::string decode_name = "default DecodeInterceptor";

using namespace ImageKnifePro;
namespace ImageKnifeTest {
class DecodeInterceptorTest : public DecodeInterceptor {
public:
    DecodeInterceptorTest()
    {
        name = "DecodeInterceptorTest";
    }

    bool Resolve(std::shared_ptr<ImageKnifeTask> task) override
    {
        if (task->product.imageBuffer != nullptr) {
            return Decode(task);
        }
        return false;
    }

    void Cancel(std::shared_ptr<ImageKnifeTask> task) override
    {
        task->cancelInfo = "Cancel Decode Success";
    }

private:
    /**
     * 进行自定义的解码实现，这里使用系统解码接口做示例
     * 本示例仅处理自定义格式（使用webp格式模拟），其他格式直接拒绝，交由责任链后续解码器处理
     */
    bool Decode(std::shared_ptr<ImageKnifeTask> task)
    {
        decode_name = name;
        auto fileTypeInfo = task->GetFileTypeInfo();
        if (fileTypeInfo == nullptr) {
            task->EchoError("FileTypeInfo is null");
            return false;
        }
        if (fileTypeInfo->format != ImageFormat::CUSTOM_FORMAT && fileTypeInfo->customFormat != "Custom Webp") {
            return false;
        }

        /**
         * 获取解码源source，这是在自定义文件格式判断时创建设置的，这里进行复用
         * source也可以本地临时创建使用，但进行复用能避免动图逐帧解码时的重复开销
         * 详见：./custom_file_type.h
         */
        auto source = GetDecodeImageSource(task);
        if (source == nullptr) {
            return false;
        }

        // 根据指定的帧数index解码，-1为批量解码所有帧，大于等于0则解码具体帧
        if (task->GetDecodeFrameIndex() == -1 && fileTypeInfo->frameCount > 1) {
            return DecodeAllFrame(task, source);
        } else {
            return DecodeFrame(task, task->GetDecodeFrameIndex(), source);
        }
    }

    OH_ImageSourceNative *GetDecodeImageSource(std::shared_ptr<ImageKnifeTask> task)
    {
        auto imageSource = std::dynamic_pointer_cast<CustomDecodeImageSource>(task->GetDecodeImageSource());
        if (imageSource == nullptr) {
            return nullptr;
        }

        return imageSource->GetImageSourceNative();
    }

    bool DecodeAllFrame(std::shared_ptr<ImageKnifeTask> task, OH_ImageSourceNative *source)
    {
        const uint32_t &frameCount = task->GetFileTypeInfo()->frameCount;
        if (frameCount > 1) {
            OH_PixelmapNative **pixelmapList = new OH_PixelmapNative* [frameCount];
            OH_DecodingOptions *decodeOption = nullptr;
            OH_DecodingOptions_Create(&decodeOption);
            if (OH_ImageSourceNative_CreatePixelmapList(source, decodeOption, pixelmapList, frameCount)) {
                task->EchoError("Create Pixelmap List Failed");
                OH_DecodingOptions_Release(decodeOption);
                delete [] pixelmapList;
                return false;
            }
            task->product.imageData = std::make_shared<ImageData>(pixelmapList,
                                                                  task->GetFileTypeInfo()->delayTimeList, frameCount);
            OH_DecodingOptions_Release(decodeOption);
            return true;
        }
        return false;
    }

    bool DecodeFrame(std::shared_ptr<ImageKnifeTask> task, uint32_t index, OH_ImageSourceNative *source)
    {
        OH_DecodingOptions *decodeOption = nullptr;
        OH_DecodingOptions_Create(&decodeOption);
        OH_DecodingOptions_SetIndex(decodeOption, index);

        OH_PixelmapNative *pixelmap = nullptr;
        if (OH_ImageSourceNative_CreatePixelmap(source, decodeOption, &pixelmap)) {
            task->EchoError("Create Pixelmap Failed");
            OH_DecodingOptions_Release(decodeOption);
            return false;
        }
        task->product.imageData = std::make_shared<ImageData>(pixelmap);
        OH_DecodingOptions_Release(decodeOption);
        return true;
    }
};

static napi_value DecoderInterceptorGetName(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_create_string_utf8(env, decode_name.c_str(), decode_name.length(), &result);
    return result;
}

/**
 * 在全局默认loader上添加解码拦截器
 */
static napi_value SetDecoderInterceptor(napi_env env, napi_callback_info info)
{
    auto imageLoader = ImageKnifePro::ImageKnifeLoader::CreateDefaultImageLoader();
    auto interceptor = new DecodeInterceptorTest();
    imageLoader->AddDecodeInterceptor(std::shared_ptr<DecodeInterceptorTest> (interceptor));
    ImageKnifePro::ImageKnife::GetInstance().SetDefaultImageKnifeLoader(imageLoader);
    return nullptr;
}

/**
 * 在新的loader上添加解码拦截器，并注册loader
 */
static napi_value SetDecodeInterceptorWithNewLoader(napi_env env, napi_callback_info info)
{
    auto imageLoader = ImageKnifePro::ImageKnifeLoader::CreateDefaultImageLoader();
    imageLoader->AddDecodeInterceptor(std::make_shared<DecodeInterceptorTest>());
    ImageKnifePro::ImageKnife::GetInstance().RegisterLoader("loaderForCustomDecode", imageLoader);
    return nullptr;
}

napi_value DecoderInterceptorSetupTests(napi_env env, napi_value &exports)
{
    napi_property_descriptor desc[] = {
        { "SetDecoderInterceptor", nullptr, SetDecoderInterceptor, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "DecoderInterceptorGetName", nullptr, DecoderInterceptorGetName,
            nullptr, nullptr, nullptr, napi_default, nullptr },
        { "setDecodeInterceptorWithNewLoader", nullptr, SetDecodeInterceptorWithNewLoader,
            nullptr, nullptr, nullptr, napi_default, nullptr },
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);

    return exports;
}
}